package com.xiaoxiang.gateway.config;

import com.xiaoxiang.common.exception.DefaultException;
import com.xiaoxiang.gateway.annotation.WebFluxFeignClient;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;

/**
 * @author: haha
 * @date: 2022/3/31 22:27
 */
public class WebFluxFeignProxy<T> implements InvocationHandler {


    private WebClient webClient;

    private Class<T> feignInterface;

    public WebFluxFeignProxy(WebClient webClient, Class<T> feignInterface) {
        this.webClient = webClient;
        this.feignInterface = feignInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
        String serviceName = getServiceName(feignInterface);
        for (Annotation annotation : declaredAnnotations) {
            if (annotation instanceof PostMapping) {
                PostMapping mapping = (PostMapping) annotation;
                return webClient.post()
                        .uri("http://" + serviceName + mapping.value()[0])
                        .body(BodyInserters.fromFormData(getParams(method, args)))
                        .retrieve().bodyToMono(getReturnType(method));
            }
        }
        return null;
    }

    private MultiValueMap<String, String> getParams(Method method, Object[] args) {
        Parameter[] parameters = method.getParameters();
        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
        for (int i = 0; i < parameters.length; i++) {
            String actualParamName = parameters[i].getName();
            Annotation[] annotations = parameters[i].getDeclaredAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation instanceof RequestParam) {
                    RequestParam param = (RequestParam) annotation;
                    actualParamName = param.value();
                    break;
                }
            }
            formData.add(actualParamName, String.valueOf(args[i]));
        }
        return formData;
    }

    private Class<?> getReturnType(Method method) {
        Type[] arguments = ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments();
        return (Class<?>)arguments[0];
    }

    private String getServiceName(Class<T> feignInterface) {
        Annotation[] annotations = feignInterface.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation instanceof WebFluxFeignClient) {
                WebFluxFeignClient feignClient = (WebFluxFeignClient) annotation;
                return feignClient.value();
            }
        }
        throw new DefaultException("WebFluxFeignClient 注解缺少指定服务名称");
    }
}
